package me.zachwhaley.android.provider;

import java.util.ArrayList;
import java.util.Arrays;

import me.zachwhaley.android.provider.ListContract.Lists;
import me.zachwhaley.android.provider.ListContract.Things;
import me.zachwhaley.android.provider.ListDatabase.Tables;
import me.zachwhaley.android.utils.SelectionBuilder;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;

public class ListProvider extends ContentProvider
{
	private static final String TAG = "ListProvider";
	private static final boolean LOGV = true;

	private ListDatabase list_db;

	private static final UriMatcher uriMatcher = buildUriMatcher();

	public static final int LISTS = 100;
	public static final int LISTS_ID = 110;

	public static final int THINGS = 200;
	public static final int THINGS_ID = 210;

	@Override
	public boolean onCreate()
	{
		list_db = new ListDatabase(getContext());
		return true;
	}

	@Override
	public String getType(Uri uri)
	{
		final int match = uriMatcher.match(uri);
		switch (match)
		{
			case LISTS:
				return Lists.CONTENT_TYPE;
			case LISTS_ID:
				return Lists.CONTENT_ITEM_TYPE;
			case THINGS:
				return Things.CONTENT_TYPE;
			case THINGS_ID:
				return Things.CONTENT_ITEM_TYPE;
			default:
				throw new UnsupportedOperationException("Unknown uri: " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values)
	{
		if (LOGV)
			Log.v(TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
		
		final SQLiteDatabase db = list_db.getWritableDatabase();
		final int match = uriMatcher.match(uri);

		switch (match)
		{
			case LISTS:
			{
				db.insertOrThrow(Tables.LISTS, null, values);
				getContext().getContentResolver().notifyChange(uri, null);
				return Lists.buildListUri(values.getAsString(BaseColumns._ID));
			}
			case THINGS:
			{
				db.insertOrThrow(Tables.THINGS, null, values);
				getContext().getContentResolver().notifyChange(uri, null);
				return Things.buildThingUri(values.getAsString(BaseColumns._ID));
			}
			default:
			{
				throw new UnsupportedOperationException("Unknown uri: " + uri);
			}
		}
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder)
	{
		if (LOGV)
			Log.v(TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
		
		final SQLiteDatabase db = list_db.getReadableDatabase();

		final SelectionBuilder builder = buildSimpleSelection(uri);
		return builder.where(selection, selectionArgs).query(db, projection, sortOrder);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs)
	{
		if (LOGV)
			Log.v(TAG, "update(uri=" + uri + ", values(" + values.toString() + "))");
		
		final SQLiteDatabase db = list_db.getWritableDatabase();
		final SelectionBuilder builder = buildSimpleSelection(uri);
		int retVal = builder.where(selection, selectionArgs).update(db, values);
		getContext().getContentResolver().notifyChange(uri, null);
		return retVal;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs)
	{
		if (LOGV)
			Log.v(TAG, "delete(uri=" + uri + ")");
		
		final SQLiteDatabase db = list_db.getWritableDatabase();
		final SelectionBuilder builder = buildSimpleSelection(uri);
		int retVal = builder.where(selection, selectionArgs).delete(db);
		getContext().getContentResolver().notifyChange(uri, null);
		return retVal;
	}

	/**
	 * Apply the given set of {@link ContentProviderOperation}, executing inside
	 * a {@link SQLiteDatabase} transaction. All changes will be rolled back if
	 * any single one fails.
	 */
	@Override
	public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
			throws OperationApplicationException
	{
		final SQLiteDatabase db = list_db.getWritableDatabase();
		db.beginTransaction();
		try
		{
			final int numOperations = operations.size();
			final ContentProviderResult[] results = new ContentProviderResult[numOperations];
			for (int i = 0; i < numOperations; i++)
			{
				results[i] = operations.get(i).apply(this, results, i);
			}
			db.setTransactionSuccessful();
			return results;
		}
		finally
		{
			db.endTransaction();
		}
	}

	/**
	 * Build a simple {@link SelectionBuilder} to match the requested
	 * {@link Uri}. This is usually enough to support {@link #insert},
	 * {@link #update}, and {@link #delete} operations.
	 * 
	 * @param uri
	 * @return SelectionBuilder
	 */
	private SelectionBuilder buildSimpleSelection(Uri uri)
	{
		final SelectionBuilder builder = new SelectionBuilder();
		final int match = uriMatcher.match(uri);

		switch (match)
		{
			case LISTS:
				return builder.table(Tables.LISTS);
			case LISTS_ID:
				final String listsId = Lists.getListId(uri);
				return builder.table(Tables.LISTS).where(BaseColumns._ID + "=?", listsId);
			case THINGS:
				return builder.table(Tables.THINGS);
			case THINGS_ID:
				final String thingsId = Things.getThingId(uri);
				return builder.table(Tables.THINGS).where(BaseColumns._ID + "=?", thingsId);
			default:
				throw new UnsupportedOperationException("Unknown uri: " + uri);
		}
	}

	private static UriMatcher buildUriMatcher()
	{
		final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
		final String authority = ListContract.CONTENT_AUTHORITY;

		matcher.addURI(authority, "lists", LISTS);
		matcher.addURI(authority, "lists/*", LISTS_ID);

		matcher.addURI(authority, "things", THINGS);
		matcher.addURI(authority, "things/*", THINGS_ID);

		return matcher;
	}

}
